# -*- coding: utf-8 -*-
import os.path
import sys
import re
import renyare.util as util
from renyare.wsgi.error import RenyareError, RenyareWarning

import xml.dom.minidom as dom

class BaseHandler:
	"""
	Base class for a handlers, that are used in Layers
	Describes basic functions.
	"""
	failed = False
	own_response = None
	def __init__(self, module, module_type, **kwargs):
		"""
		Parameters:
			config - a config file dictionary in saxdict format
			pass_args - arguments to pass to the module
			wsgi - higher level wsgi data [environ, start_response, orig_app]
				orig_app - original application, that had to be run before we
				started handling
			module - module name to process
		"""
		# WSGI Procedures
		self.config = kwargs.get('config') or None
		self.orig_app = kwargs.get('original_app') or None
		self.module = module
		self.module_type = module_type

	def __call__(self, environ, start_response):
		"""
		Runs main handler functions. Please, modifying the code of this
		function in derived classes is NOT recommended.
		Change handle_start and handle_end_(un)parsed functions instead.
		"""
		# WSGI Procedures
		self.environ = environ
		self.start_response = start_response # save original response anyways
		# Detect own response
		if(not self.own_response):
			response = start_response
		else:
			response = self.own_response
		try:
			status = '200 OK'
			headers = [('Content-type','text/xml')]
			self.handleStart() # Prepare content for output
			response(status, headers)

		except RunnerError(text): # Process error from handler separately
			status = '200 OK'
			headers = [('Content-type','text/plain')]
			self.start_response(status, headers)
			raise RunnerError("Error in handled application (preparation stage): "+str(sys.exc_info()[1]))

		except:
			status = '200 OK'
			headers = [('Content-type','text/plain')]
			self.start_response(status, headers)
			import traceback, cStringIO
			out = cStringIO.StringIO()
			traceback.print_exc(file=out)
			raise RunnerError("Error in handler application (preparation stage): "+str(out.getvalue())+'; '+str(sys.exc_info())[1])

		# No excepts worked=>no errors happen on preparation stage
		# Start responsing
		try:
			for d in self.finish_response():
				yield d
		except:
			raise RunnerError("Error in handler (response stage): " + str(sys.exc_info()[1]))

	def handleStart(self):
		"""
		Overload this to prepare application for request pending
		This function return value is ignored. Error can be raised and will be
		trapped.
		"""
		None

	def handle_end_parsed(self):
		"""
		Function called after start_response and after handle_end_unparsed
		It should return a reference to parsed content for further xml
		processing
		"""
		if(self.data):
			try:
				xml = dom.parseString(self.data)
				node = environ['renyare.application']['node']
				node.importNode(xml.documentElement, True)
			except:
				self._err("Parsing error: %s" %str(self.data))

	def handle_end_unparsed(self):
		"""
		Function called to get unparsed content to output
		"""
		return self.data

	def finish_response(self):
		# In any way we need unparsed content
		ret = self.handle_end_unparsed()
		if(isinstance(ret, (str, unicode))): # String returned
			if(not hasattr(self, 'data')): self.data = ret # self.data can be set before in child class procedures
			if(self.environ.get('renyare.require_parsed') == True):
				self.handle_end_parsed()
			else:
				yield self.data

		else: # Iterable returned
			if(self.environ.get('renyare.require_parsed') == True):
				self.handle_end_parsed()
#			else:
			for d in ret:
				yield d

	def _err(self, text=None):
		"""
		Should be called to process application errors
		"""
		import traceback as tb
		t = ""
		for x in tb.format_exception(*sys.exc_info()):
			t += x
		raise RenyareWarning(text+"\n"+"-"*20+"\n Original application error: " +t)


class RunnerError(RenyareError):
	pass
